using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using Microsoft.Win32;
using ResourceBlender.DAL;
using ResourceBlender.Express.Properties;
using ResourceBlender.Core;

namespace ResourceBlender.Express {
    /// <summary>
    /// The main window
    /// </summary>
    public partial class WinMain : Window {
        private bool haveDatabase = false;

        public string[] CommandLineArgs { get; set; }

        /// <summary>
        /// Initializes a new <see cref="WinMain"/> and sets up command bindings.
        /// </summary>
        public WinMain() {
            InitializeComponent();
            LoadConfig();
            this.CommandBindings.Add(new CommandBinding(ResourceBlenderCommands.AutoTranslate, AutoTranslate, CanExecuteOtherCommands));
            this.CommandBindings.Add(new CommandBinding(ResourceBlenderCommands.CreateVariant, CreateVariant, CanExecuteOtherCommands));
            this.CommandBindings.Add(new CommandBinding(ResourceBlenderCommands.DuplicateApplication, DuplicateApplication, CanExecuteOtherCommands));
            this.CommandBindings.Add(new CommandBinding(ResourceBlenderCommands.Export, Export, CanExecuteOtherCommands));
            this.CommandBindings.Add(new CommandBinding(ResourceBlenderCommands.GenerateResources, GenerateResources, CanExecuteOtherCommands));
            this.CommandBindings.Add(new CommandBinding(ResourceBlenderCommands.Import, Import, CanExecuteOtherCommands));
            this.CommandBindings.Add(new CommandBinding(ResourceBlenderCommands.OpenRBT, OpenRBT));
            this.CommandBindings.Add(new CommandBinding(ResourceBlenderCommands.OpenResx, OpenResx));
            this.CommandBindings.Add(new CommandBinding(ResourceBlenderCommands.ShowAbout, ShowAbout));
            this.CommandBindings.Add(new CommandBinding(ResourceBlenderCommands.ShowDuplicates, ShowDuplicateResources, CanExecuteOtherCommands));
            this.CommandBindings.Add(new CommandBinding(ResourceBlenderCommands.ShowOptions, ShowOptions));
            this.CommandBindings.Add(new CommandBinding(ResourceBlenderCommands.ShowSummary, ShowSummary, CanExecuteOtherCommands));
            this.CommandBindings.Add(new CommandBinding(ResourceBlenderCommands.ShowTemplates, ShowTemplates, CanExecuteOtherCommands));
            this.CommandBindings.Add(new CommandBinding(ResourceBlenderCommands.ShowUnused, ShowUnusedResources, CanExecuteOtherCommands));
            this.CommandBindings.Add(new CommandBinding(ResourceBlenderCommands.ViewApplications, ViewApplications, CanExecuteOtherCommands));
            this.CommandBindings.Add(new CommandBinding(ResourceBlenderCommands.ViewResourcesPrompt, ViewResourcesPrompt, CanExecuteOtherCommands));

            this.CommandBindings.Add(new CommandBinding(ApplicationCommands.Close, (s, e) => { Close(); }));
            this.CommandBindings.Add(new CommandBinding(ApplicationCommands.Help, (s, e) => { WPFUtils.OpenHelp(null); }));

            this.CommandBindings.Add(new CommandBinding(ResourceBlenderCommands.EditDefaultLanguage, EditDefaultLanguage, CanExecuteOtherCommands));
            this.CommandBindings.Add(new CommandBinding(ResourceBlenderCommands.EditAllLanguages, EditAllLanguages, CanExecuteOtherCommands));
            this.CommandBindings.Add(new CommandBinding(ResourceBlenderCommands.Edit, EditApplication, CanExecuteEditApplication));
            this.CommandBindings.Add(new CommandBinding(ResourceBlenderCommands.Delete, DeleteApplication, CanExecuteDeleteApplication));
            //Console.WriteLine(I18nUtils.GetString("UIStrings", "AppElementBundleSummary", I18nUtils.GetString("UIStrings", "AddElement"), I18nUtils.GetString("UIStrings", "AddElement")));
            //Console.WriteLine(I18nUtils.GetString("UIStrings", "AppElementBundleSummary","a","b","c","b","c"));
        }

        /// <summary>
        /// Reloads the configuration from persistent storage.
        /// </summary>
        private void LoadConfig() {
            WPFConfiguration.LoadConfig();
        }

        #region Command Handlers
        /// <summary>
        /// Shows the import dialog.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
        private void Import(object sender, ExecutedRoutedEventArgs e) {
            WinImport dlg = new WinImport((Constants.DataType)Enum.Parse(typeof(Constants.DataType), e.Parameter.ToString(), true));
            dlg.Owner = this;
            if(dlg.ShowDialog() == true) {
                RefreshSummary();
            }
        }

        /// <summary>
        /// Shows the import dialog.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
        private void Export(object sender, ExecutedRoutedEventArgs e) {
            WinExport dlg = new WinExport((Constants.DataType)Enum.Parse(typeof(Constants.DataType), e.Parameter.ToString(), true));
            dlg.Owner = this;
            dlg.ShowDialog();
        }

        /// <summary>
        /// Determines whether there is a valid database connection available which can be used.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.Input.CanExecuteRoutedEventArgs"/> instance containing the event data.</param>
        private void CanExecuteOtherCommands(object sender, CanExecuteRoutedEventArgs e) {
            e.CanExecute = haveDatabase;
        }

        /// <summary>
        /// Determines whether the edit application command can be executed.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.Input.CanExecuteRoutedEventArgs"/> instance containing the event data.</param>
        private void CanExecuteEditApplication(object sender, CanExecuteRoutedEventArgs e) {
            e.CanExecute = haveDatabase && lstApps != null && lstApps.SelectedItem != null;
        }

        /// <summary>
        /// Shows a window to edit an application.
        /// </summary>
        private void EditApplication(object sender, RoutedEventArgs e) {
            if(lstApps.SelectedValue == null)
                return;
            ObservableApplication originalApp = ((KeyValuePair<string, ObservableApplication>)lstApps.SelectedValue).Value;
            WinAddApplication dlg = new WinAddApplication(originalApp.Clone());
            dlg.Owner = this;
            if(dlg.ShowDialog() == true) {
                RefreshSummary();
            }
        }

        /// <summary>
        /// Determines whether the delete application command can be executed.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.Input.CanExecuteRoutedEventArgs"/> instance containing the event data.</param>
        private void CanExecuteDeleteApplication(object sender, CanExecuteRoutedEventArgs e) {
            e.CanExecute = haveDatabase && lstApps != null && lstApps.SelectedItem != null;
        }

        /// <summary>
        /// Deletes an application after the user has confirmed.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
        private void DeleteApplication(object sender, ExecutedRoutedEventArgs e) {
            if(lstApps.SelectedValue == null)
                return;
            ObservableApplication app = ((KeyValuePair<string, ObservableApplication>)lstApps.SelectedValue).Value;
            if(DialogBox.Show(this, string.Format("Are you sure you want to delete the application '{0}'?\n\nAll bundles and elements contained within this application will also be deleted. Translations, will be not be deleted.", app.DisplayName), "Confirm Delete", DialogBox.DialogBoxType.YesNo, DialogBox.DialogBoxIcon.Question) == DialogBox.DialogBoxButton.Yes) {
                BackgroundWorker worker = new BackgroundWorker();
                worker.DoWork += (s, dwe) => {
                    ResourceBlenderDAL.DeleteApplication(app);
                };
                worker.RunWorkerCompleted += (s, rwe) => {
                    SetStatus(null);
                    if(rwe.Error != null) {
                        DialogBox.Show(this, rwe.Error.Message, "Database Error", DialogBox.DialogBoxType.OK, DialogBox.DialogBoxIcon.Error);
                    }
                    RefreshSummary();
                };
                SetStatus("Deleting application...");
                worker.RunWorkerAsync();
            }
        }

        /// <summary>
        /// Show the 'Automatic Translation' window.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
        private void AutoTranslate(object sender, RoutedEventArgs e) {
            WinAutoTranslate dlg = new WinAutoTranslate();
            dlg.Owner = this;
            dlg.ShowDialog();
        }

        /// <summary>
        /// Opens the browser to the donate page.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
        private void btnDonate_Click(object sender, RoutedEventArgs e) {
            System.Diagnostics.Process.Start("http://www.resourceblender.com/donate");
        }

        /// <summary>
        /// Shows the 'Create Variant' window.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
        private void CreateVariant(object sender, RoutedEventArgs e) {
            WinCreateVariant dlg = new WinCreateVariant();
            dlg.Owner = this;
            dlg.ShowDialog();
        }

        /// <summary>
        /// Opens an RBT translations database for editing.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
        private void OpenResx(object sender, RoutedEventArgs e) {
            OpenFileDialog dlgOpen = new OpenFileDialog();
            dlgOpen.DefaultExt = ".resx";
            dlgOpen.CheckPathExists = true;
            dlgOpen.Filter = "All files|*.*|.NET Managed Resource Files (*.resx)|*.resx";
            dlgOpen.FilterIndex = 2;
            dlgOpen.FileName = Properties.Settings.Default.LastImportFile;
            dlgOpen.Title = "Choose file";
            if(dlgOpen.ShowDialog() == true) {
                WinEditResx dlg = new WinEditResx(dlgOpen.FileName);
                dlg.Owner = this;
                dlg.ShowDialog();
            }
        }

        /// <summary>
        /// Opens an RBT translations database for editing.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
        private void OpenRBT(object sender, RoutedEventArgs e) {
            OpenFileDialog dlgOpen = new OpenFileDialog();
            dlgOpen.DefaultExt = ".rbt";
            dlgOpen.CheckPathExists = true;
            dlgOpen.Filter = "All files|*.*|Translation Databases (*.rbt)|*.rbt";
            dlgOpen.FilterIndex = 2;
            dlgOpen.FileName = Properties.Settings.Default.LastImportFile;
            dlgOpen.Title = "Choose file";
            if(dlgOpen.ShowDialog() == true) {
                WinEditRBT dlg = new WinEditRBT(dlgOpen.FileName);
                dlg.Owner = this;
                dlg.ShowDialog();
            }
        }

        /// <summary>
        /// Shows the 'Generates Resources' dialog.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.Input.ExecutedRoutedEventArgs"/> instance containing the event data.</param>
        private void GenerateResources(object sender, ExecutedRoutedEventArgs e) {
            WinGenerate dlg = new WinGenerate() { Owner = this };
            if(e.Parameter is KeyValuePair<string, ObservableApplication>) {
                dlg.SelectedApplication = ((KeyValuePair<string, ObservableApplication>)e.Parameter).Value;
            }
            dlg.Show();
        }

        /// <summary>
        /// Duplicates an application.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.Input.ExecutedRoutedEventArgs"/> instance containing the event data.</param>
        private void DuplicateApplication(object sender, ExecutedRoutedEventArgs e) {
            ObservableApplication app = ((KeyValuePair<string, ObservableApplication>)e.Parameter).Value;

            BackgroundWorker worker = new BackgroundWorker();
            worker.DoWork += (s, dwe) => {
                ResourceBlenderDAL.DuplicateApplication(app);
            };
            worker.RunWorkerCompleted += (s, rwe) => {
                SetStatus(null);
                if(rwe.Error != null) {
                    DialogBox.Show(this, rwe.Error.Message, "Database Error", DialogBox.DialogBoxType.OK, DialogBox.DialogBoxIcon.Error);
                }
                RefreshSummary();
            };
            SetStatus("Duplicating application...");
            worker.RunWorkerAsync();
        }

        /// <summary>
        /// Shows the 'About' dialog.
        /// </summary>
        private void ShowAbout(object sender, RoutedEventArgs e) {
            WinAbout dlg = new WinAbout();
            dlg.Owner = this;
            dlg.ShowDialog();
        }

        /// <summary>
        /// Opens the 'Duplicate Resources' window.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.Input.ExecutedRoutedEventArgs"/> instance containing the event data.</param>
        private void ShowDuplicateResources(object sender, ExecutedRoutedEventArgs e) {
            WinDuplicateResources dlg = new WinDuplicateResources();
            dlg.Owner = this;
            dlg.ShowDialog();
        }

        /// <summary>
        /// Shows the options dialog, then reloads settings.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
        private void ShowOptions(object sender, RoutedEventArgs e) {
            WinOptions dlg = new WinOptions();
            dlg.Owner = this;
            dlg.ShowDialog();
            Properties.Settings.Default.Reload();
            WPFConfiguration.LoadConfig();
            RefreshSummary();
        }

        /// <summary>
        /// Shows a resource summary.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
        private void ShowSummary(object sender, ExecutedRoutedEventArgs e) {
            if(e.Parameter == null) {
                // prompt for app IDs
                WinSelectApplication dlgSelect = new WinSelectApplication("Resource Summary", "View Summary", false);
                dlgSelect.Owner = this;
                dlgSelect.ShowDialog();
                if(dlgSelect.DialogResult == true) {
                    List<string> applicationIds = new List<string>(dlgSelect.SelectedApplications.Count);
                    foreach(ObservableApplication app in dlgSelect.SelectedApplications) {
                        applicationIds.Add(app.ID);
                    }
                    WinCompletionSummary dlg = new WinCompletionSummary(applicationIds.ToArray(), dlgSelect.IncludeNotInUse);
                    dlg.Owner = this;
                    dlg.Show();
                }
            } else {
                WinCompletionSummary dlg = new WinCompletionSummary(new string[]  { ((ObservableApplication)e.Parameter).ID }, true);
                dlg.Owner = this;
                dlg.Show();
            }
        }

        /// <summary>
        /// Shows the 'Manage Templates' dialog.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
        private void ShowTemplates(object sender, RoutedEventArgs e) {
            WinTemplates dlg = new WinTemplates();
            dlg.Owner = this;
            dlg.ShowDialog();
        }

        /// <summary>
        /// Opens the 'Unused Resources' window.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.Input.ExecutedRoutedEventArgs"/> instance containing the event data.</param>
        private void ShowUnusedResources(object sender, ExecutedRoutedEventArgs e) {
            WinUnusedResources dlg = new WinUnusedResources();
            dlg.Owner = this;
            dlg.ShowDialog();
        }

        /// <summary>
        /// Shows a window for managing applications
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
        private void ViewApplications(object sender, RoutedEventArgs e) {
            WinApplications dlg = new WinApplications();
            dlg.Owner = this;
            dlg.ShowDialog();
            RefreshSummary();
        }

        /// <summary>
        /// Shows a prompt to select an application, bundles and languages.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
        private void ViewResourcesPrompt(object sender, RoutedEventArgs e) {
            WinResourcesGridPrompt dlg = new WinResourcesGridPrompt();
            dlg.Owner = this;
            dlg.ShowDialog();
            if(dlg.DialogResult == true) {
                ViewResources(dlg.SelectedApplication, dlg.SelectedBundles, dlg.SelectedLanguages);
                RefreshSummary();
            }
        }

        #endregion

        /// <summary>
        /// Checks whether this is the first time the application has been run and shows a setup wizard installer if necessary.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
        private void Window_Loaded(object sender, RoutedEventArgs e) {
            if(CommandLineArgs != null && CommandLineArgs.Length > 0) {
                RefreshSummary();
                WinEditRBT dlg = new WinEditRBT(CommandLineArgs[0]);
                dlg.Owner = this;
                dlg.ShowDialog();
            } else {
                if(Properties.Settings.Default.FirstRun) {
                    WinInstaller dlg = new WinInstaller();
                    dlg.Owner = this;
                    dlg.ShowDialog();
                    switch(dlg.DialogResult) {
                        case true: // database setup successful
                            WPFConfiguration.LoadConfig();
                            RefreshSummary();
                            break;
                        case false: // open an RBT file
                            WPFConfiguration.LoadConfig();
                            RefreshSummary();
                            ResourceBlenderCommands.OpenRBT.Execute(null, this);
                            break;
                        default: // setup not completed
                            Close();
                            break;
                    }
                } else {
                    RefreshSummary();
                }
            }
        }

        #region Summary
        /// <summary>
        /// Refreshes the application summary.
        /// </summary>
        private void RefreshSummary() {
            BackgroundWorker worker = new BackgroundWorker();
            string niceName = string.Empty;
            worker.DoWork += (s, dwe) => {
                try {
                    niceName = Database.NiceName;
                    dwe.Result = ResourceBlenderDAL.GetApplications(true);
                } catch(Exception ex) {
                    throw new Exception("Error #462: Could not connect to database - " + ex.Message);
                }
            };
            worker.RunWorkerCompleted += (s, rwe) => {
                SetStatus(null);
                if(rwe.Error != null) {
                    haveDatabase = false;
                    lblDb.Text = "No database connection";
                    txtDBError.Text = rwe.Error.Message;
                    tbcSummary.SelectedIndex = 2;
                } else {
                    haveDatabase = true;
                    lblDb.Text = niceName;
                    List<RBApplication> applications = rwe.Result as List<RBApplication>;
                    SortedList<string, ObservableApplication> apps = new SortedList<string, ObservableApplication>();
                    if(applications.Count > 0) {
                        tbcSummary.SelectedIndex = 0;
                        foreach(RBApplication app in applications) {
                            apps.Add(app.DisplayName, new ObservableApplication(app));
                        }
                        lstApps.ItemsSource = apps;
                    } else {
                        tbcSummary.SelectedIndex = 1;
                    }
                    CommandManager.InvalidateRequerySuggested();
                }
            };
            SetStatus("Loading application summary...");
            worker.RunWorkerAsync();
        }

        /// <summary>
        /// Edits the default language in the selected application.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.Input.ExecutedRoutedEventArgs"/> instance containing the event data.</param>
        private void EditDefaultLanguage(object sender, ExecutedRoutedEventArgs e) {
            ObservableApplication selectedApp = e.Parameter as ObservableApplication;
            if(selectedApp == null) {
                return;
            }
            ObservableSortedDictionary<string, CultureInfo> languages = new ObservableSortedDictionary<string, CultureInfo>(new Comparers.CultureInfoEnglshNameComparer(), new Comparers.StringEqualityComparer());
            CultureInfo info = Utils.GetCultureInfo(selectedApp.DefaultLanguage);
            if(info != null) {
                languages.Add(info.Name, info);
            }
            if(languages.Count == 0) {
                DialogBox.ShowAlert(this, "At least one language and one bundle must be added before editing resources.", "Error");
            } else {
                ViewResources(selectedApp, selectedApp.Bundles, languages);
            }
        }

        /// <summary>
        /// Edits all languages in the selected application.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.Input.ExecutedRoutedEventArgs"/> instance containing the event data.</param>
        private void EditAllLanguages(object sender, ExecutedRoutedEventArgs e) {
            ObservableApplication selectedApp = e.Parameter as ObservableApplication;
            if(selectedApp == null) {
                return;
            }
            ObservableSortedDictionary<string, CultureInfo> languages = new ObservableSortedDictionary<string, CultureInfo>(new Comparers.CultureInfoEnglshNameComparer(), new Comparers.StringEqualityComparer());
            foreach(string code in selectedApp.Languages) {
                CultureInfo info = Utils.GetCultureInfo(code);
                if(info != null) {
                    languages.Add(code, info);
                }
            }
            if(languages.Count == 0) {
                DialogBox.ShowAlert(this, "At least one language and one bundle must be added before editing resources.", "Error");
            } else {
                ViewResources(selectedApp, selectedApp.Bundles, languages);
            }
        }
        #endregion

        /// <summary>
        /// Sets the text on the status message (null hides the statusbar and label).
        /// </summary>
        /// <param name="message">The message.</param>
        private void SetStatus(string message) {
            if(message == null) {
                prgStatus.Visibility = Visibility.Collapsed;
                lblStatus.Visibility = Visibility.Collapsed;
            } else {
                prgStatus.Visibility = Visibility.Visible;
                lblStatus.Visibility = Visibility.Visible;
                lblStatus.Text = message;
            }
        }

        /// <summary>
        /// Shows a window to view/edit resources for a selected application.
        /// </summary>
        /// <param name="app">The app.</param>
        /// <param name="selectedBundles">The selected bundles.</param>
        /// <param name="selectedLanguages">The selected languages.</param>
        private void ViewResources(ObservableApplication app, ObservableSortedDictionary<string, string> selectedBundles, ObservableSortedDictionary<string, CultureInfo> selectedLanguages) {
            WinResourcesGrid dlg = new WinResourcesGrid(app, selectedBundles, selectedLanguages);
            dlg.Owner = this;
            dlg.ShowDialog();
        }
    }
}
